<?php

namespace Kollway\CodeGenerator\Services;

class YamlService
{
    // yaml 配置的表
    private $tables = [];

    // yaml 配置的字段
    private $fields = [];

    private $error = '';

    // 允许出现关键字
    private static $allow_keywords = [
        'list_name',
        'required_fields',
        'search_fields',
        'search_placeholder',
        'form_title',
        'form_type',

        'passwords',
        'radios',
        'checkboxes',
        'images',
        'textareas',
    ];

    private static $form_allow_fields = [
        'start_index',
        'values'
    ];

    // 检查是否有无法识别字段, 格式是否正确
    public function checkSyntax($array)
    {
        // 必须是关联数组
        if (!$this->isAssocArray($array)) {
            return $this->error;
        }
        if ($this->tableDuplicate()) {
            return $this->error;
        }
        if ($this->illegalKeywordOrValueExists($array)) {
            return $this->error;
        }
        if ($this->fieldDuplicate($array)) {
            return $this->error;
        }

        $this->tables = array_keys($array);

        return true;
    }

    // 判断字段是否重复配置了
    private function fieldDuplicate($array)
    {
        foreach ($array as $table => $config) {
            foreach ($config as $key => $value) {
                if (in_array($key, ['required_fields', 'search_fields'])) {
                    if (is_array($value)) {
                        $this->fields = array_merge($this->fields, $value);
                    } else if (!empty($value)) {
                        $this->fields[] = $value;
                    }
                } elseif (in_array($key, ['passwords', 'images', 'textareas'])) {
                    if (is_array($value)) {
                        $this->fields = array_merge($this->fields, $value);
                    }
                } elseif (in_array($key, ['radios', 'checkboxes'])) {
                    if (is_array($value)) {
                        $this->fields = array_merge($this->fields, array_keys($value));
                    }
                }
            }
        }

        if (count($this->fields) != count(array_values($this->fields))) {
            // 有重复的表, 找出重复的表名
            $temp = [];
            foreach ($this->fields as $field) {
                if (in_array($field, $temp)) {
                    $this->error = "字段: $field 重复配置";
                } else {
                    $temp[] = $field;
                }
            }

            return true;
        }

        return false;
    }

    // 判断是否有无法识别字段
    private function illegalKeywordOrValueExists($array)
    {
        foreach ($array as $table => $config) {
            foreach ($config as $key => $value) {
                if (!in_array($key, static::$allow_keywords)) {
                    $this->error = "非法关键字: $key";
                    return true;
                } else if (in_array($key, ['radios', 'checkboxes'])) {
                    if (is_array($value)) {
                        if (!$this->isAssocArray($value)) {
                            $this->error = "$table 中 $key 的配置值必须是关联数组";
                            return true;
                        }

                        foreach ($value as $k => $v) {
                            foreach ($v as $k1 => $v1) {
                                if (!in_array($k1, static::$form_allow_fields)) {
                                    $this->error = "$table 的 $key 中存在非法键: $k1";
                                    return true;
                                }
                            }
                        }
                    }
                }
            }
        }

        return false;
    }

    /**
     * 判断 yaml 配置中是否有重复配置的表
     * @return bool 有重复返回 true, 否则返回 false
     */
    private function tableDuplicate()
    {
        if (count($this->tables) != count(array_values($this->tables))) {
            // 有重复的表, 找出重复的表名
            $temp = [];
            foreach ($this->tables as $table) {
                if (in_array($table, $temp)) {
                    $this->error = "表: $table 重复配置";
                } else {
                    $temp[] = $table;
                }
            }

            return true;
        }

        return false;
    }

    /**
     * 检查数组是否是关联数组
     * @param array $array 要检查的数组
     * @return bool 是关联数组返回 true, 否则返回 false
     */
    private function isAssocArray($array)
    {
        $keys = array_keys($array);
        foreach ($keys as $key) {
            if (is_numeric($key)) {
                $this->error = '键: ' . $key . '非法';
                return false;
            }
        }

        return true;
    }
}